Test 3: Caso 2D - Cavidad

En el flujo con una cavidad rectangular, el fluido se encuentra confinado en una región rectangular de dimensiones $L\times W$ donde el movimiento se debe al desplazamiento transversal de una de las caras. En este sentido, la velocidad de la cara superior es de $U_0$ y el de las demás interfaces es de $0$. De acuerdo con esto, se tienen las siguientes consideraciones:

  • El flujo se presenta únicamente en las direcciones $x$ y $y$ ($w=0$).
  • El flujo no depende de la coordenada $z$ ($\frac{\partial}{\partial z}=0$).
  • El fluido se encuentra en estado estacionario ($\frac{\partial}{\partial t}=0$).
  • No hay fuerzas externas afectando el flujo ($f_x=f_y=f_z=0$).

De aquí, las ecuaciones de continuidad y de Navier-Stokes se reducen a:

\begin{align} \frac{\partial \rho}{\partial t} + \nabla\cdot(\rho \vec{u})=0 \quad &\to\quad \frac{\partial u}{\partial x}+\frac{\partial v}{\partial y}=0 \\ \rho\left(\frac{\partial \vec{u}}{\partial t}+\vec{u}\cdot\nabla\vec{u}\right)=\rho\vec{f}-\nabla p+\mu\nabla^2\vec{u}\quad &\to\quad \begin{cases} \rho \left(u\frac{\partial u}{\partial x}+v\frac{\partial u}{\partial y}\right)=-\frac{\partial p}{\partial x}+\mu\left(\frac{\partial^2u}{\partial x^2}+\frac{\partial^2u}{\partial y^2}\right) \\ \rho \left(u\frac{\partial v}{\partial x}+v\frac{\partial v}{\partial y}\right)=-\frac{\partial p}{\partial v}+\mu\left(\frac{\partial^2v}{\partial x^2}+\frac{\partial^2v}{\partial y^2}\right) \end{cases} \end{align}

Se puede ver entonces que la ecuación resultante es un caso particular del problema de convección-difusión estable:

$$\frac{\partial f}{\partial t} + \vec{u}\cdot\nabla f=k\nabla^2f+\frac{s}{\rho c_p}$$

Donde $\frac{\partial f}{\partial t}=0$, $k = \frac{\mu}{\rho}=\nu$, y $\frac{s}{c_p}=-\frac{\partial p}{\partial *}$ para $f=u, *=x$ y $f=v, *=y$, y las condiciones de frontera corresponden a valores conocidos (tipo Dirichlet).

Ahora, para aplicar el método de elementos espectrales, es necesario realizar los siguientes pasos:

  • Introducción de la información del problema
  • Definición de los elementos a usar (cantidad $N_E$ y distribución, así como sus órdenes $N_P(i)$)
    • Selección de los nodos de interpolación a usar
    • Generación de la matriz de conectividad
  • Generación de la matriz del problema
    • Generación de la matriz de difusión y el vector b
  • Solución del sistema resultante
  • Gráfica de la solución obtenida

Cada uno de los pasos se tratará en detalle a continuación


In [1]:
# Prepara el ambiente de trabajo con las librerías a utilizar

import numpy as np               # Ofrece funciones para operaciones básicas con arreglos
import scipy.linalg as lin       # Ofrece funciones para operaciones de álgebra lineal
import matplotlib.pyplot as plt  # Permite incluir un ambiente de visualización
from matplotlib.patches import Patch
from matplotlib.collections import PatchCollection
from mpl_toolkits.mplot3d.axes3d import Axes3D
import timeit                    # Permite la medición de tiempos para los distintos algoritmos
from sem2D import *               # Agrupa las funciones externas en un archivo
# from __future__ import division  # Corrige posibles problemas que surgen a la hora de dividir enteros
#%pylab inline

Introducción de la información del problema

Se introducen los parámetros del problema. En este caso se tienen los parámetros $L$, $W$ y $U_0$.


In [2]:
## Valores de prueba

L = 2.4
W = 1.6
mu = 1.
U0 = 1.

Definición de los elementos a usar

Se define primero el número de elementos y su distribución, así como los órdenes de cada uno de los mismos. Por simplicidad, inicialmente se toman elementos uniformemente espaciados, y se toma el orden de las subdivisiones como $m \leq 3$


In [3]:
Ne = np.array([12,8])   # Indica la cantidad de elementos a lo largo de los ejes x y y
m = 3   # Indica la cantidad de divisiones sobre cada elemento en el plano xi-eta (admite valores 1, 2 y 3)

# Genera los puntos correspondientes a los vertices de cada rectángulo
(px, hx) = np.linspace(-L/2,L/2,Ne[0]+1,retstep=True)
(py, hy) = np.linspace(-W/2,W/2,Ne[1]+1,retstep=True)

Selección de los nodos de interpolación a usar

Conociendo el número de elementos y su distribución, es posible generar los nodos correspondientes a los vértices de cada elemento. Sin embargo, como no se trabaja con elementos lineales, es necesario generar los nodos de interpolación para cada elemento. Para ello, es necesario definir la familia de polinomios sobre la cual van a estar basados estos nodos. En este caso se utilizan los nodos de interpolación de Lobatto.

def genNodesTri(Ne,m,px,py): #================================================= # Genera los nodos de interpolacion segun el # numero de elementos y sus ordenes para una malla # triangular. Esta es la numeracion local de los # nodos: (l,i) # # m < 4 #================================================= ne = 2*Ne[0]*Ne[1] # Recupera el numero de elementos npe = (m+1)*(m+2)/2 # Este es el numero de nodos por elemento x = np.zeros([ne,npe]) # Coordenadas x por elemento y = np.zeros([ne,npe]) # Coordenadas y por elemento # Genera los nodos de interpolacion de Lobatto distr = lobatto(m-1) for l in range(ne/2): # Desarrolla dos elementos por iteracion cx = l % Ne[0] cy = l / Ne[0] pxe = 0.5 * ((px[cx+1]+px[cx])+(px[cx+1]-px[cx])*distr) pye = 0.5 * ((py[cy+1]+py[cy])+(py[cy+1]-py[cy])*distr) # Elemento inferior x[2*l,0:3*m] = np.concatenate([[px[cx]],pxe,[px[cx+1]],pxe[::-1], px[cx]*np.ones(m)]) y[2*l,0:3*m] = np.concatenate([py[cy]*np.ones(m+1), pye,[py[cy+1]],pye[::-1]]) if(m==3): x[2*l,9] = (2.*px[cx]+px[cx+1])/3. y[2*l,9] = (2.*py[cy]+py[cy+1])/3. # Elemento superior x[2*l+1,0:3*m] = np.concatenate([[px[cx+1]],pxe[::-1],[px[cx]],pxe, px[cx+1]*np.ones(m)]) y[2*l+1,0:3*m] = np.concatenate([[py[cy]],pye,py[cy+1]*np.ones(m+1), pye[::-1]]) if(m==3): x[2*l+1,9] = (2.*px[cx+1]+px[cx])/3. y[2*l+1,9] = (2.*py[cy+1]+py[cy])/3. return x, y, ne, npe

In [4]:
(x, y, ne, npe) = genNodesTri(Ne,m,px,py)

# Grafica la malla de puntos
for l in range(ne):
    plt.plot([x[l,0],x[l,m],x[l,2*m],x[l,0]],
             [y[l,0],y[l,m],y[l,2*m],y[l,0]],'r--')
    plt.hold(True)
plt.plot(x,y,'bo',ms=4)
plt.xlabel('coordenada x')
plt.ylabel('coordenada y')
plt.show()

Generación de la matriz de conectividad

Una vez se definen los nodos, se utiliza la matriz de conectividad para almacenar de manera ordenada la numeración local y global de los mismos

def genConn(Ne,Npm,ne,npe,x,y,L,W,U0,h,tipo='quad'): #================================================= # Genera los vectores de numeracion global, la # matriz de conectividad y el vector de frontera # # max(Np) < 6 (quad) o m < 4 (tri) #================================================= # Calcula el numero de nodos globales para definir el tamano de las matrices contenedoras if(tipo == 'quad'): ng = ne*npe - ((Ne[0]-1)*(Npm[1]+1)+(Ne[1]-1)*(Npm[0]+1)+(Ne[0]-1)*(Ne[1]-1)*(sum(Npm)+1)) elif(tipo == 'tri'): ng = ne*npe - ((ne/2+Ne[0]+Ne[1]-2)*(Npm+1)+(Ne[0]-1)*(Ne[1]-1)*(2*Npm+1)) coords = np.zeros([ng,2]) # Almacena las coordenadas de todos los nodos en un vector sin redundancia C = np.zeros((ne,npe),int) # Matriz de conectividad gfl = np.zeros([ng,3]) # Indicador de nodos de frontera [indicador, vel. x, vel. y] # Se introduce manualmente el primer nodo coords[0,0] = x[0,0] coords[0,1] = y[0,0] # C[0,0] = 0 Esta condicion ya se tiene de la inicializacion de C gfl[0,0] = 1 # Indica un nodo de frontera (el valor de la velocidad es de [0,0]) cont = 1 # Contador global tol = h*1e-6 # Tolerancia para identificacion de nodos iguales for l in range(ne): for k in range(npe): existe = False for n in range(cont): if(np.abs(coords[n,0]-x[l,k])+np.abs(coords[n,1]-y[l,k]) < tol): existe = True C[l,k] = n # El nodo (l,m) corresponde al nodo global n if(not existe): # Registra el nodo si no existe todavia coords[cont,0] = x[l,k] coords[cont,1] = y[l,k] C[l,k] = cont # Identifica los nodos de frontera y actualiza el indicador if (L/2.-np.abs(x[l,k]) < tol or W/2.-np.abs(y[l,k]) < tol): gfl[cont,0] = 1 # Si se encuentra en la frontera superior actualiza la velocidad x if(W/2.-y[l,k] < tol): gfl[cont,1] = U0 cont += 1 return coords, C, gfl, ng

In [5]:
h = min(hx,hy)
(coords, C, gfl, ng) = genConn(Ne,m,ne,npe,x,y,L,W,U0,h,tipo='tri')

Generación de la matriz del problema

Para la generación de la matriz del problema es necesario definir las matrices de difusión, masa y advección por cada elemento:

$$A_{ij}^{(\ell)}=\iint_{E_\ell} \nabla\psi_i^{(\ell)}\cdot\nabla\psi_j^{(\ell)}dxdy \quad B_{ij}^{(\ell)}=\iint_{E_\ell} \psi_i^{(\ell)}\psi_j^{(\ell)}dxdy \quad C_{ij}^{(\ell)}=\iint_{E_\ell} \psi_i^{(\ell)}\vec{u}\cdot\nabla\psi_j^{(\ell)}dxdy$$

Estas integrales se obtienen utilizando la cuadratura de Gauss triangular aprovechando la geometría sencilla del dominio. Dado que el orden máximo de las funciones de interpolación es 3, bastaría con usar la cuadratura de orden 4. Puntualmente, usando las funciones ortogonales de Proriol las funciones de interpolación se pueden describir como:

$$ \psi_i(\xi,\eta) = \frac{\text{Det}[V_\phi(\xi_1,\eta_1,\xi_2,\eta_2,\dots,\xi,\eta,\dots,\xi_N,\eta_N)]}{\text{Det}[V_\phi(\xi_1,\eta_1,\xi_2,\eta_2,\dots,\xi_i,\eta_i,\dots,\xi_N,\eta_N)]} $$

Generación de la matriz de difusión

Comenzamos con la matriz que define el sistema de ecuaciones, o la matriz de difusión. Dado que esta depende de los gradientes de las funciones de interpolación, es necesario realizar todos los pasos de construcción de los términos como sigue.

Calculo de las derivadas de las funciones de interpolación

De acuerdo con esta descripción, se tiene entonces que las derivadas con respecto a $\xi$ y $\eta$ se obtienen derivando los polinomios de proriol. Esto radica en que las únicas entradas no constantes son las que corresponden a las entradas $\xi$ y $\eta$ del numerador.

Calculo de la función de transformación y sus derivadas

De este modo, la función de transformación de coordenadas corresponde a:

$$ \vec{x} = \sum_{i=0}^{N_p-1} \vec{x}_i^E \psi_i(\xi,\eta) = \sum_{i=0}^{N_p-1} \vec{x}_i^E \psi_{(j,k)}(\xi,\eta) = \sum_{i=0}^{N_p-1} \vec{x}_i^E \mathcal{L}_j(\xi)\mathcal{W}_k(\eta) $$

y por lo tanto sus derivadas son:

\begin{align} \frac{\partial x}{\partial \xi} &= \sum_{i=0}^{N_p-1}x_i^E\frac{\partial\psi_{(j,k)}(\xi,\eta)}{\partial\xi} &\quad \frac{\partial x}{\partial \eta} &= \sum_{i=0}^{N_p-1}x_i^E\frac{\partial\psi_{(j,k)}(\xi,\eta)}{\partial\eta} \\ \frac{\partial y}{\partial \xi} &= \sum_{i=0}^{N_p-1}y_i^E\frac{\partial\psi_{(j,k)}(\xi,\eta)}{\partial\xi} &\quad \frac{\partial y}{\partial \eta} &= \sum_{i=0}^{N_p-1}y_i^E\frac{\partial\psi_{(j,k)}(\xi,\eta)}{\partial\eta} \end{align}

Calculo de los gradientes de las funciones de interpolación

Ya con estos valores es posible soluciones los gradientes de las funciones solucionando los sistemas:

$$ \begin{pmatrix} \frac{\partial x}{\partial \xi} & \frac{\partial y}{\partial \xi} \\ \frac{\partial x}{\partial \eta} & \frac{\partial y}{\partial \eta} \end{pmatrix} \nabla\psi_i = {\frac{\partial\psi_i}{\partial\xi} \choose \frac{\partial\psi_i}{\partial\xi}} $$

Calculo del factor de corrección (cambio de base) $h_s$

De manera que el coeficiente de cambio de coordenadas corresponde a:

$$ Det[J] = \begin{vmatrix} \frac{\partial x}{\partial \xi} & \frac{\partial x}{\partial \eta} \\ \frac{\partial y}{\partial \xi} & \frac{\partial y}{\partial \eta} \end{vmatrix} = \frac{\partial x}{\partial \xi}\frac{\partial y}{\partial \eta}-\frac{\partial x}{\partial \eta}\frac{\partial y}{\partial \xi} $$

Generación de las matrices de presión

Ahora bien, como tampoco se conocen los valores de presión, es necesario incorporarlas también como incógnitas. Para ello se calculan los vectores: $$ D_{i}^{(\ell)}=\iint_{E_\ell} \nabla\psi_i^{(\ell)}dxdy$$

Generación del vector b

Para el otro lado de la ecuación se implementa la función de entrada. Para esto se recorre sobre todos los elementos y se van calculando los valores para la función de manera correspondiente. Estos valores se acomodan al sistema global aprovechando la matriz de conectividad.

Implementación de las condiciones de frontera

Así mismo, es necesario modificar la matriz global de manera que se incorporen las condiciones de frontera de Dirichlet. Si $l$ es un nodo de frontera, esto se hace a través de los pasos:

  • Sustituir las entradas $b_i$ por $b_i-D_{il}f_l$
  • Cambiar el término $b_l$ por $f_l$
  • Igualar las entradas de la fila $l$ y columna $l$ por 0 en la matriz global
  • Reajustar el término $D_{ll}$ a $1$.

Así, se utilizan las funciones auxiliares (precalculando los valores para los polinomios requeridos) siguientes:

def proriol(k,l,xi,eta): #================================================= # Evalua los polinomios de Proriol usando las # formas precalculadas # # Admite polinomios de orden hasta 3: k+l < 4 #================================================= if(k==0 and l==0): val = 1 elif(k==1 and l==0): val = 2*xi+eta -1 elif(k==0 and l==1): val = 3*eta -1 elif(k==2 and l==0): val = 6*xi**2+6*xi*eta+eta**2 -6*xi-2*eta +1 elif(k==1 and l==1): val = (2*xi+eta -1)*(5*eta -1) elif(k==0 and l==2): val = 10*eta**2 -8*eta +1 elif(k==3 and l==0): val = (2*xi+eta -1)*(10*xi**2+10*xi*eta+eta**2 -10*xi-2*eta +1) elif(k==2 and l==1): val = (6*xi**2+6*xi*eta+eta**2 -6*xi-2*eta +1)*(7*eta-1) elif(k==1 and l==2): val = (2*xi+eta -1)*(21*eta**2 -12*eta +1) elif(k==0 and l==3): val = 35*eta**3 -45*eta**2 +15*eta -1 else: raise NameError('La cantidad de elementos no coincide con el orden') return val def dproriol(k,l,xi,eta,var='xi'): #================================================= # Evalua las derivadas de los polinomios de # Proriol usando las formas precalculadas # # Admite polinomios de orden hasta 3: k+l < 4 #================================================= if(var=='xi'): if(k==0 and l==0): val = 0 elif(k==1 and l==0): val = 2 elif(k==0 and l==1): val = 0 elif(k==2 and l==0): val = 12*xi+6*eta-6 elif(k==1 and l==1): val = 2*(5*eta -1) elif(k==0 and l==2): val = 0 elif(k==3 and l==0): val = 2*(10*xi**2+10*xi*eta+eta**2 -10*xi-2*eta +1)+(2*xi+eta -1)*(20*xi+10*eta-10) elif(k==2 and l==1): val = (12*xi+6*eta-6)*(7*eta-1) elif(k==1 and l==2): val = 2*(21*eta**2 -12*eta +1) elif(k==0 and l==3): val = 0 else: raise NameError('La cantidad de elementos no coincide con el orden') elif(var=='eta'): if(k==0 and l==0): val = 0 elif(k==1 and l==0): val = 1 elif(k==0 and l==1): val = 3 elif(k==2 and l==0): val = 6*xi+2*eta-2 elif(k==1 and l==1): val = (5*eta -1)+(2*xi+eta -1)*5 elif(k==0 and l==2): val = 20*eta-8 elif(k==3 and l==0): val = (10*xi**2+10*xi*eta+eta**2 -10*xi-2*eta +1)+(2*xi+eta -1)*(10*xi+2*eta-2) elif(k==2 and l==1): val = (6*xi+2*eta-2)*(7*eta-1)+(6*xi**2+6*xi*eta+eta**2 -6*xi-2*eta +1)*7 elif(k==1 and l==2): val = (21*eta**2 -12*eta +1)+(2*xi+eta -1)*(42*eta-12) elif(k==0 and l==3): val = 105*eta**2-90*eta +15 else: raise NameError('La cantidad de elementos no coincide con el orden') else: raise NameError('Variable de derivacion invalida') return val
def genVDM(m): #================================================= # Evalua la matriz de Vandermonde generalizada, su # determinante y la matriz de cofactores # # Admite polinomios de orden hasta 3: k+l < 4 #================================================= # Recupera las posiciones estandar de los nodos npe = (m+1)*(m+2)/2 distr = 0.5+0.5*lobatto(m-1) xist = np.zeros(npe) etast = np.zeros(npe) xist[0:3*m] = np.concatenate([[0],distr,[1],distr[::-1],np.zeros(m)]) etast[0:3*m] = np.concatenate([np.zeros(m+1),distr,[1],distr[::-1]]) if(m==3): xist[9] = 1./3. etast[9] = 1./3. # Inicializa las matrices requeridas para el calculo Mat_VDM = np.zeros([npe,npe]) Mat_Cof = np.zeros([npe,npe]) Mat_Min = np.zeros([npe-1,npe-1]) prind = np.array([[0,1,0,2,1,0,3,2,1,0], [0,0,1,0,1,2,0,1,2,3]]) # Construye la matriz de Vandermonde generalizada for i in range(npe): for j in range(npe): k = prind[0,i] l = prind[1,i] Mat_VDM[i,j] = proriol(k,l,xist[j],etast[j]) Det_VDM = lin.det(Mat_VDM) for i in range(npe): for j in range(npe): Temp = np.delete(Mat_VDM,i,0) Mat_Min = np.delete(Temp,j,1) Mat_Cof[i,j] = (-1)**(i+j)*np.linalg.det(Mat_Min) return Mat_VDM, Det_VDM, Mat_Cof
def matDiffTri(Ne,m,ne,npe,ng,x,y,C,gfl,mu=1): #================================================= # Genera la matriz de difusion/rigidez y el vector # del lado derecho del sistema sujeto a condicio- # nes de Dirichlet para el caso triangular # # m < 4 #================================================= # Define los nodos a usar en la cuadratura NQ = 7 # Numero de nodos a usar en la cuadratura (1,3,4,6,7,9,12,13) (xiq, etaq, wq) = gaussTri(NQ) (Mat_VDM, Det_VDM, Mat_Cof) = genVDM(m) prind = np.array([[0,1,0,2,1,0,3,2,1,0], [0,0,1,0,1,2,0,1,2,3]]) Mat_dif = np.zeros([2*ng+ne,2*ng+ne]) # Inicializa la matriz de difusion global for ell in range(ne): elm_dm = np.zeros([npe,npe]) # Inicializa la matriz de difusion elemental vecD = np.zeros([npe,2]) # Realiza la cuadratura de Gauss for nq in range(NQ): psi = np.ones(npe) # Almacena las funciones de interpolacion dpsixi = np.zeros(npe) # Almacena las derivadas con respecto a xi dpsieta = np.zeros(npe) # Almacena las derivadas con respecto a eta grpsi = np.zeros([npe,2]) # Almacena los gradientes de las funciones de interpolacion dxdxi = 0. dxdeta = 0. dydxi = 0. dydeta = 0. for i in range(npe): #Realiza los calculos sobre cada elemento # Calcula las funciones de interpolacion vect = np.zeros(npe) for j in range(npe): k = prind[0,j] l = prind[1,j] vect[j] = proriol(k,l,xiq[nq],etaq[nq]) psi[i] = np.dot(vect,Mat_Cof[:,i])/Det_VDM # Calcula las derivadas con respecto a las dos variables vect = np.zeros(npe) for j in range(npe): k = prind[0,j] l = prind[1,j] vect[j] = dproriol(k,l,xiq[nq],etaq[nq],'xi') dpsixi[i] = np.dot(vect,Mat_Cof[:,i])/Det_VDM vect = np.zeros(npe) for j in range(npe): k = prind[0,j] l = prind[1,j] vect[j] = dproriol(k,l,xiq[nq],etaq[nq],'eta') dpsieta[i] = np.dot(vect,Mat_Cof[:,i])/Det_VDM # print dpsixi[i], dpsieta[i] # Actualiza los valores de las derivadas de la funcion de transformacion dxdxi += x[ell,i]*dpsixi[i] dxdeta += x[ell,i]*dpsieta[i] dydxi += y[ell,i]*dpsixi[i] dydeta += y[ell,i]*dpsieta[i] # print dxdxi,dxdeta,dydxi,dydeta for i in range(npe): # Calcula el gradiente para cada funcion de interpolacion grpsi[i,:] = lin.solve(np.array([[dxdxi,dydxi],[dxdeta,dydeta]]), np.array([dpsixi[i],dpsieta[i]])) # Calcula el factor de correccion hs = np.abs(dxdxi*dydeta-dxdeta*dydxi) # Construye la matriz de difusion elemental for i in range(npe): for j in range(npe): elm_dm[i,j] += np.dot(grpsi[i,:],grpsi[j,:])*(0.5*hs*wq[nq]) # Construye los vectores D vecD[i,:] += grpsi[i,:]*(0.5*hs*wq[nq]) # Actualiza la matriz de difusion global Mat_dif[np.meshgrid(C[ell,:],C[ell,:],indexing='ij')] += mu*elm_dm Mat_dif[np.meshgrid(ng+C[ell,:],ng+C[ell,:],indexing='ij')] += mu*elm_dm # Extensiones Mat_dif[C[ell,:],2*ng+ell] -= vecD[:,0] #incorpora Dx en el bloque B Mat_dif[ng+C[ell,:],2*ng+ell] -= vecD[:,1] #incorpora Dy en el bloque B Mat_dif[2*ng+ell,C[ell,:]] -= vecD[:,0] #incorpora Dx en el bloque C Mat_dif[2*ng+ell,ng+C[ell,:]] -= vecD[:,1] #incorpora Dy en el bloque C # Construye el vector del lado derecho vec_b = np.zeros(2*ng+ne) # Implementa las condiciones de frontera for ind in range(ng): if(gfl[ind,0]==1): # Ajusta las entradas al lado derecho vec_b -= Mat_dif[:,ind]*gfl[ind,1] + Mat_dif[:,ng+ind]*gfl[ind,2] # Ajusta el valor de la entrada en b vec_b[ind] = gfl[ind,1] # Coor. x vec_b[ng+ind] = gfl[ind,2] # Coor. y # Ajusta la matriz del problema Mat_dif[:,ind] = np.zeros(2*ng+ne) Mat_dif[:,ng+ind] = np.zeros(2*ng+ne) Mat_dif[ind,:] = np.zeros(2*ng+ne) Mat_dif[ng+ind,:] = np.zeros(2*ng+ne) Mat_dif[ind,ind] = 1. Mat_dif[ng+ind,ng+ind] = 1. return Mat_dif, vec_b

In [6]:
(Mat_dif, vec_b) = matDiffTri(Ne,m,ne,npe,ng,x,y,C,gfl,mu)

plt.spy(Mat_dif)
plt.show()

Solución del sistema resultante

Teniendo ya construidos los componentes del sistema a resolver, basta aplicar un esquema de solución razonable para el tipo de matriz obtenida. En este caso se utiliza el solucionador por defecto del módulo linalg. Dado que los valores para el primer y último nodo son conocidos, se omiten los valores correspondientes.


In [7]:
start = timeit.default_timer()
sols = lin.solve(Mat_dif[1:,1:],vec_b[1:])
print timeit.default_timer()-start

# Recupera las velocidades
solx = np.concatenate([[gfl[0,1]], sols[0:ng-1]])
soly = sols[ng-1:2*ng-1]

# Calcula la magnitud de la velocidad en cada nodo
solm = np.abs(solx**2+soly**2)

# Recupera las presiones
solp = sols[2*ng-1:]


0.687418899406

Gráfica de la solución obtenida

Una vez resuelto el sistema, es posible visualizar el resultado obtenido por medio de una gráfica. En este caso se conoce que la solución analítica corresponde a la linea recta entre las dos condiciones extremas, y se incluye también su gráfica.


In [8]:
# Grafica las fronteras de los elementos
for l in range(ne):
    plt.plot([x[l,0],x[l,m],x[l,2*m],x[l,0]],
             [y[l,0],y[l,m],y[l,2*m],y[l,0]],
             'r--')
    plt.hold(True)
    

    
# Grafica el campo de velocidades
plt.quiver(coords[:,0],coords[:,1],solx,soly,color='k')
plt.title('Campo de Velocidades para el Flujo en la Cavidad')
plt.xlabel('Posicion (coord x)')
plt.ylabel('Posicion (coord y)')
plt.xlim([-1.2, 1.2])
plt.ylim([-1.2, 1.2])
plt.show()

In [9]:
# Grafica el campo de presiones
xpres = np.zeros(ne)
ypres = np.zeros(ne)
for l in range(ne):
    xpres[l] = (x[l,0]+x[l,m]+x[l,2*m])/3.0
    ypres[l] = (y[l,0]+y[l,m]+y[l,2*m])/3.0
zpres = solp-np.mean(solp)   # Como se trabaja con el gradiente es importante
                             # la manera en que cambia, no su valor puntual
    
fig = plt.figure()
ax = fig.gca(projection='3d')
# Grafica las fronteras de los elementos
for l in range(ne):
    plt.plot([x[l,0],x[l,m],x[l,2*m],x[l,0]],
             [y[l,0],y[l,m],y[l,2*m],y[l,0]],
             'r--')
    ax.plot([xpres[l],xpres[l]],[ypres[l],ypres[l]],[0,zpres[l]],'b',linewidth=2)
    plt.hold(True)

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('Presion')

plt.show()

In [ ]: